package glin

import (
	"errors"
	"net"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"time"

	"github.com/gorilla/sessions"
)

// 设置session
type SessionConfig struct {
	Secretkey string //session的秘钥
	SessionId string //默认的sessionID
	MaxAge    int    //单位秒，默认30分
}

// 后台的鉴权配置文件
type AuthConfig struct {
	MaxAge int //最大有效时间，单位秒 ,默认30分钟
}

// zhangycsz 新增的配置文件
type Config struct {
	Path        string        //当前项目的文件路径
	Name        string        //项目名称
	LogPathDir  string        //日志文件夹路径
	LogMaxDay   int           //日志文件夹的文件最大的日期,单位天
	TempPathDir string        //上传的临时文件夹
	Session     SessionConfig //session的配置文件
	Auth        AuthConfig    //后台的配置文件
}

// 全局的配置文件
var GDefaultConfig *Config

// 创建默认的配置文件
func NewDefaultConfig() *Config {
	if GDefaultConfig == nil {
		_path := GetCurrPath()
		_name := GetCurrName()
		ses := SessionConfig{Secretkey: "glin" + _name, SessionId: "gsession", MaxAge: 30 * 60}
		GDefaultConfig = &Config{
			LogMaxDay:   7,
			Path:        _path,
			Name:        _name,
			LogPathDir:  _path + "/log_" + _name,
			TempPathDir: _path + "/tmp",
			Auth:        AuthConfig{MaxAge: 30 * 60},
			Session:     ses,
		}

	}
	return GDefaultConfig
}

// 加载配置默认文件
func LoadConfig() {
	if StoreSessions == nil {
		scookie := sessions.NewCookieStore([]byte(GDefaultConfig.Session.Secretkey))
		scookie.MaxAge(GDefaultConfig.Session.MaxAge)
		scookie.Options.HttpOnly = true
		StoreSessions = scookie
	}
}

// 设置日志路径
func (conf *Config) SetLogPathDir(logPathDir string) {
	conf.LogPathDir = logPathDir
}

// 设置日志的开始执行检查
func (conf *Config) SetLogMaxDay(day int) {
	conf.LogMaxDay = day
}

// 得到当前的项目名称
func GetCurrPath() string {
	_path := ""
	if len(os.Args) > 0 {
		_path = os.Args[0]
	}
	if _path != "" {
		if strings.Contains(_path, "go-build") {
			//说明是测试环境
			return "./"
		}
		fileName := filepath.Base(_path)
		_path = _path[0 : len(_path)-len(fileName)]
		return _path
	}
	ex, err := os.Executable()
	if err != nil {
		return "./"
	}
	if strings.Contains(_path, "go-build") {
		//说明是测试环境
		return "./"
	}
	fileName := filepath.Base(ex)
	ex = ex[0 : len(ex)-len(fileName)]
	return ex
}

// 得到项目的文件名称
func GetCurrName() string {
	name := "test"
	_path := ""
	if len(os.Args) > 0 {
		_path = os.Args[0]
	}
	if _path != "" {
		if strings.Contains(_path, "go-build") { //说明是测试环境
			return name
		}
		name = filepath.Base(_path)
		return name
	}
	ex, err := os.Executable()
	if err != nil {
		return name
	}

	name = filepath.Base(ex)
	return name
}

// 得到本机IP
func GetLocalIp() string {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return "127.0.0.1"
	}
	for _, addr := range addrs {
		if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
			if ipnet.IP.To4() != nil {
				return ipnet.IP.String()
			}
		}
	}
	return "127.0.0.1"
}

// 创建目录
func MkdirAll(dir string) error {
	return os.MkdirAll(dir, 0755)
}

// 检查文件或目录是否存在
func CheckExists(path string) bool {
	_, err := os.Stat(path)
	return !os.IsNotExist(err)
}

// 删除html的空格
func MinifyHTML(html string) string {
	lines := strings.Split(html, "\n")
	var result []string
	for _, line := range lines {
		// 如果去除空白后还有内容，则保留该行
		if strings.TrimSpace(line) != "" {
			result = append(result, line)
		}
	}
	return strings.Join(result, "\n")
}

// 初始化信息
func init() {
	NewDefaultConfig()
	GLog = NewLog("glin")
	//需要检查服务器日志文件
	go checkLogDar()
}

func checkLogDar() {
	for {
		checkTempDir(GDefaultConfig.LogPathDir, int64(GDefaultConfig.LogMaxDay*24*60*60))
		checkTempDir(GDefaultConfig.TempPathDir, int64(GDefaultConfig.LogMaxDay*24*60*60))
		time.Sleep(time.Hour * 12)
	}
}

// 删除临时文件
func checkTempDir(delDirPath string, maxDay int64) error {
	//文件夹
	cuix := time.Now().Unix()
	logdirs, err := os.ReadDir(delDirPath)
	if err != nil {
		return err
	}
	for _, loffir := range logdirs {
		//属于文件
		if loffir.IsDir() {
			nextDirPath := delDirPath + "/" + loffir.Name()
			checkTempDir(nextDirPath, maxDay)
			continue
		}
		file, err := loffir.Info()
		if err != nil {
			continue
		}
		crimt := file.ModTime().UTC().Unix() + maxDay
		if crimt < cuix {
			c := delDirPath + "/" + loffir.Name()
			os.Remove(c)
		}
	}
	return err
}

// 自动注册结构图路由
// 结构图方法，如果是get_,post_put_delete_等前缀的方法，会自动识别为对应的http方法
// 自动添加结构体，自动检查mvc的路径,用&controller{},
// param 参数一般用于/:id/:name
func AutoRegister(r *Echo, controller interface{}, param string) (err error) {
	ctrlType := reflect.TypeOf(controller)
	ctrlValue := reflect.ValueOf(controller)
	if ctrlType.Kind() != reflect.Ptr {
		err = errors.New("不是指针")
		return
	}
	controllerName := strings.ToLower(ctrlType.Elem().Name())
	modelName := "" //模块的名称
	pathName := reflect.TypeOf(controller).String()
	if pathName != "" {
		pathName = strings.Replace(pathName, "*", "", -1)
		arr := strings.Split(pathName, ".")
		if len(arr) > 0 {
			modelName = arr[0]
		}
	}
	if modelName != "" {
		if modelName == "app" {
			modelName = ""
		}
	}
	if modelName != "" {
		modelName = "/" + modelName
	}

	for i := 0; i < ctrlType.NumMethod(); i++ { //遍历结构体中的方法

		method := ctrlType.Method(i)
		methodValue := ctrlValue.Method(i)
		actionName := method.Name
		_method := ""
		if actionName != "" {
			actionName = strings.ToLower(actionName)
			arr := strings.Split(actionName, "_")
			if len(arr) > 0 {
				if arr[0] == "get" || arr[0] == "post" || arr[0] == "put" || arr[0] == "delete" {
					_method = arr[0]
					actionName = arr[1]
				}
			}
		}
		urlPath := modelName + "/" + controllerName + "/" + actionName + param

		if method.Type.NumIn() == 2 {

			hanler := createEchoHandler(methodValue)
			switch _method {
			case "get":
				r.GET(urlPath, hanler)
			case "post":
				r.POST(urlPath, hanler)
			case "put":
				r.PUT(urlPath, hanler)
			case "delete":
				r.DELETE(urlPath, hanler)
			default:
				r.GET(urlPath, hanler)
				r.POST(urlPath, hanler)
			}
		}

	}

	return nil
}

// 自动注册分组信息
// group:分组ID，
// controller:结构体
// param 参数一般用于/:id/:name
func AutoRegisterGroup(r *Group, controller interface{}, param string) (err error) {
	ctrlType := reflect.TypeOf(controller)
	ctrlValue := reflect.ValueOf(controller)
	if ctrlType.Kind() != reflect.Ptr {
		err = errors.New("不是指针")
		return
	}
	controllerName := strings.ToLower(ctrlType.Elem().Name())
	modelName := "" //模块的名称
	pathName := reflect.TypeOf(controller).String()
	if pathName != "" {
		pathName = strings.Replace(pathName, "*", "", -1)
		arr := strings.Split(pathName, ".")
		if len(arr) > 0 {
			modelName = arr[0]
		}
	}
	if modelName != "" {
		if modelName == "app" {
			modelName = ""
		}
	}
	if modelName != "" {
		modelName = "/" + modelName
	}

	for i := 0; i < ctrlType.NumMethod(); i++ { //遍历结构体中的方法

		method := ctrlType.Method(i)
		methodValue := ctrlValue.Method(i)
		actionName := method.Name
		_method := ""
		if actionName != "" {
			actionName = strings.ToLower(actionName)
			arr := strings.Split(actionName, "_")
			if len(arr) > 0 {
				if arr[0] == "get" || arr[0] == "post" || arr[0] == "put" || arr[0] == "delete" {
					_method = arr[0]
					actionName = arr[1]
				}
			}
		}
		urlPath := modelName + "/" + controllerName + "/" + actionName + param

		if method.Type.NumIn() == 2 {
			hanler := createEchoHandler(methodValue)
			switch _method {
			case "get":
				r.GET(urlPath, hanler)
			case "post":
				r.POST(urlPath, hanler)
			case "put":
				r.PUT(urlPath, hanler)
			case "delete":
				r.DELETE(urlPath, hanler)
			default:
				r.GET(urlPath, hanler)
				r.POST(urlPath, hanler)
			}
		}

	}

	return nil
}

func createEchoHandler(methodValue reflect.Value) HandlerFunc {
	return func(c Context) error {
		// 调用控制器方法
		results := methodValue.Call([]reflect.Value{reflect.ValueOf(c)})
		// 检查返回值
		if len(results) > 0 {
			if err, ok := results[0].Interface().(error); ok {
				return err
			}
		}
		return nil
	}
}
